package com.csun.dsms.service.impl;

import com.csun.dsms.comment.base.enums.ErrorCodeEnum;
import com.csun.dsms.comment.base.exception.BusinessException;
import com.csun.dsms.comment.core.support.BaseService;
import com.csun.dsms.comment.core.support.CsunMessageSource;
import com.csun.dsms.mapper.DsmsMenuMapper;
import com.csun.dsms.model.constant.MenuConstant;
import com.csun.dsms.model.domain.DsmsMenu;
import com.csun.dsms.model.domain.DsmsRoleMenu;
import com.csun.dsms.model.domain.DsmsUser;
import com.csun.dsms.model.dto.menu.*;
import com.csun.dsms.model.enums.MenuStatusEnum;
import com.csun.dsms.model.vo.menu.BindAuthVo;
import com.csun.dsms.model.vo.menu.MenuVo;
import com.csun.dsms.model.vo.menu.ViewMenuVo;
import com.csun.dsms.service.DsmsMenuService;
import com.csun.dsms.service.DsmsRoleMenuService;
import com.csun.dsms.utils.PublicUtil;
import com.csun.dsms.utils.TreeUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author chisj chisj@foxmail.com
 * @version 1.0.0
 * @date 2021-09-01 11:55
 * @description: TODO
 */
@Service
@Transactional
public class DsmsMenuServiceImpl extends BaseService<DsmsMenu> implements DsmsMenuService {

    @Resource
    private DsmsMenuMapper dsmsMenuMapper;
    @Resource
    private CsunMessageSource csunMessageSource;
    @Resource
    private DsmsRoleMenuService dsmsRoleMenuService;

    /**
     * 检测菜单编码是否已存在
     *
     * @param menuCheckCodeDto  the menu check code dto
     *
     * @return  true-the menu code is not exist.
     */
    @Override
    public boolean checkMenuCode(MenuCheckCodeDto menuCheckCodeDto) {

        Long id = menuCheckCodeDto.getMenuId();
        String menuCode = menuCheckCodeDto.getMenuCode();

        Example example = new Example(DsmsMenu.class);
        Example.Criteria criteria = example.createCriteria();

        if (id != null) {
            criteria.andNotEqualTo("id", id);
        }
        criteria.andEqualTo("menuCode", menuCode);

        int result = selectCountByExample(example);

        return result < 1;
    }

    /**
     * 检测菜单名称唯一性
     *
     * @param menuCheckNameDto  the menu check name dto
     *
     * @return  true-the menu name is not exist.
     */
    @Override
    public boolean checkMenuName(MenuCheckNameDto menuCheckNameDto) {

        Long id = menuCheckNameDto.getMenuId();
        Long pid = menuCheckNameDto.getPid();
        String menuName = menuCheckNameDto.getMenuName();

        Example example = new Example(DsmsMenu.class);
        Example.Criteria criteria = example.createCriteria();

        if (id != null) {
            criteria.andNotEqualTo("id", id);
        }
        criteria.andEqualTo("menuName", menuName);
        criteria.andEqualTo("pid", pid);

        int result = selectCountByExample(example);

        return result < 1;
    }

    /**
     * 检测菜单URL唯一性
     *
     * @param menuCheckUrlDto   the menu check url dto
     *
     * @return  true-the menu url is not exist.
     */
    @Override
    public boolean checkMenuUrl(MenuCheckUrlDto menuCheckUrlDto) {

        Long id = menuCheckUrlDto.getMenuId();
        String url = menuCheckUrlDto.getUrl();

        Example example = new Example(DsmsMenu.class);
        Example.Criteria criteria = example.createCriteria();

        if (id != null) {
            criteria.andNotEqualTo("id", id);
        }
        criteria.andEqualTo("url", url);

        int result = selectCountByExample(example);

        return result < 1;
    }

    /**
     * 获取菜单列表数据
     *
     * @return
     */
    @Override
    public List<MenuVo> getMenuVoList() {

        List<MenuVo> menuVoList = Lists.newArrayList();
        List<DsmsMenu> menuList;
        Set<DsmsMenu> menuSet = Sets.newHashSet();

        DsmsMenu menuQuery = new DsmsMenu();
        menuQuery.setStatus(MenuStatusEnum.ENABLE.getType());
        menuQuery.setOrderBy(" level asc,number asc");
        menuList = dsmsMenuMapper.selectMenuList(menuQuery);

        List<MenuVo> list = getMenuVo(menuList);
        if (PublicUtil.isNotEmpty(menuVoList)) {
            list.addAll(menuVoList);
        }
        // 2.递归成树
        return TreeUtil.getChildMenuVos(list, 0L);
    }

    /**
     * 查询菜单详情
     *
     * @param id    the menu id
     *
     * @return  the view menu vo.
     */
    @Override
    public ViewMenuVo getViewVoById(Long id) {

        Preconditions.checkArgument(PublicUtil.isNotEmpty(id),
                csunMessageSource.getMessage("menu.id.null", null));

        DsmsMenu menu = dsmsMenuMapper.selectByPrimaryKey(id);

        if (menu == null) {
            throw new BusinessException(ErrorCodeEnum.GB10011045);
        }

        // 获取父级菜单信息
        DsmsMenu parentMenu = dsmsMenuMapper.selectByPrimaryKey(menu.getPid());

        ModelMapper modelMapper = new ModelMapper();
        ViewMenuVo menuVo = modelMapper.map(menu, ViewMenuVo.class);

        if (parentMenu != null) {
            menuVo.setParentMenuName(parentMenu.getMenuName());
        }

        return menuVo;
    }

    /**
     * 查询菜单及下面所有子菜单
     *
     * @param menuQueryDto  the menu query dto
     *
     * @return  the dsms menu
     */
    @Override
    public List<DsmsMenu> queryMenuAndSubMenuListWithPage(MenuQueryDto menuQueryDto) {

        return dsmsMenuMapper.queryMenuAndSubMenuListWithPage(menuQueryDto);
    }

    /**
     * 根据id修改菜单的禁用状态
     *
     * @param menuStatusDto the menu status dto
     * @param login the login user
     *
     * @return  the int.
     */
    @Override
    public Integer updateMenuStatusById(MenuStatusDto menuStatusDto, DsmsUser login) {

        Long id = menuStatusDto.getId();
        String status = menuStatusDto.getStatus();
        Preconditions.checkArgument(id != null, "菜单ID不能为空");
        Preconditions.checkArgument(StringUtils.isNotEmpty(status), "菜单状态不能为空");

        DsmsMenu menuQuery = this.selectByKey(id);
        if (MenuConstant.MENU_LEVEL_ROOT.equals(menuQuery.getLevel())) {
            throw new BusinessException(ErrorCodeEnum.GB10011046);
        }
        // 要处理的菜单集合
        List<DsmsMenu> menuList = Lists.newArrayList();

        int result;
        if (status.equals(MenuStatusEnum.DISABLE.getType())) {
            // 获取菜单以及子菜单
            menuList = this.getAllChildMenuByMenuId(id, MenuStatusEnum.ENABLE.getType());
            // 禁用菜单以及子菜单
            result = this.disableMenuList(menuList, login);
        } else {
            // 获取菜单、其子菜单以及父菜单
            DsmsMenu dsmsMenu = new DsmsMenu();
            dsmsMenu.setPid(id);
            result = this.selectCount(dsmsMenu);
            // 此菜单含有子菜单
            if (result > 0) {
                menuList = this.getAllChildMenuByMenuId(id, MenuStatusEnum.DISABLE.getType());
            }
            List<DsmsMenu> menuListTemp = this.getAllParentMenuByMenuId(id);
            for (DsmsMenu menu : menuListTemp) {
                if (!menuList.contains(menu)) {
                    menuList.add(menu);
                }
            }
            // 启用菜单、其子菜单以及父菜单
            result = this.enableMenuList(menuList, login);
        }
        if (result < 1) {
            throw new BusinessException(ErrorCodeEnum.GB10011049);
        }

        return result;
    }

    /**
     * 新增菜单
     *
     * @param dsmsMenu  the dsms menu
     * @param login the login user
     *
     * @return  the int.
     */
    @Override
    public Integer save(DsmsMenu dsmsMenu, DsmsUser login) {

        Long pid = dsmsMenu.getPid();
        dsmsMenu.setUpdateInfo(login);

        // 校验菜单父Id是否为空
        Preconditions.checkArgument(PublicUtil.isNotEmpty(pid),
                csunMessageSource.getMessage("menu.pid.null", null));

        // 校验父节点
        DsmsMenu parentMenu = mapper.selectByPrimaryKey(pid);
        if (PublicUtil.isEmpty(parentMenu)) {
            throw new BusinessException(ErrorCodeEnum.GB10011043);
        }

        // 校验菜单URL是否为空
//		Preconditions.checkArgument(PublicUtil.isNotEmpty(menu.getUrl()),
//				csunMessageSource.getMessage("menu.url.null", null));

        if (PublicUtil.isNotEmpty(dsmsMenu.getUrl())) {
            // 校验菜单URL唯一
            MenuCheckUrlDto menuCheckUrlDto = new MenuCheckUrlDto();
            menuCheckUrlDto.setUrl(dsmsMenu.getUrl());
            Preconditions.checkArgument(checkMenuUrl(menuCheckUrlDto),
                    csunMessageSource.getMessage("menu.url.exist", null));
        }

        // 校验菜单编码是否为空
        Preconditions.checkArgument(PublicUtil.isNotEmpty(dsmsMenu.getMenuCode()),
                csunMessageSource.getMessage("menu.code.null", null));

        // 校验菜单编码是否唯一
        MenuCheckCodeDto menuCheckCodeDto = new MenuCheckCodeDto();
        menuCheckCodeDto.setMenuCode(dsmsMenu.getMenuCode());
        Preconditions.checkArgument(checkMenuCode(menuCheckCodeDto),
                csunMessageSource.getMessage("menu.code.exist", null));

        // 校验菜单名称不能为空
        Preconditions.checkArgument(PublicUtil.isNotEmpty(dsmsMenu.getMenuName()),
                csunMessageSource.getMessage("menu.name.null", null));

        DsmsMenu updateMenu = new DsmsMenu();
        dsmsMenu.setLevel(parentMenu.getLevel() + 1);
        updateMenu.setLeaf(MenuConstant.MENU_LEAF_NO);
        updateMenu.setId(pid);
        //Long menuId = super.generateId();
        //menu.setId(menuId);
        int result = mapper.updateByPrimaryKeySelective(updateMenu);
        if (result < 1) {
            throw new BusinessException(ErrorCodeEnum.GB10011043);
        }

        dsmsMenu.setStatus(MenuStatusEnum.ENABLE.getType());

        // 新增的菜单是叶子节点
        dsmsMenu.setLeaf(MenuConstant.MENU_LEAF_YES);

        return dsmsMenuMapper.insertSelective(dsmsMenu);
    }

    /**
     * 修改菜单信息
     *
     * @param dsmsMenu  the dsms menu
     * @param login the login user
     *
     * @return  the int.
     */
    @Override
    public Integer editMenu(DsmsMenu dsmsMenu, DsmsUser login) {

        // 校验Id不能为空
        Preconditions.checkArgument(dsmsMenu.getId() != null,
                csunMessageSource.getMessage("menu.id.null", null));

        // 校验菜单Id是否存在
        DsmsMenu menuQuery = dsmsMenuMapper.selectByPrimaryKey(dsmsMenu.getId());
        if (PublicUtil.isEmpty(menuQuery)) {
            throw new BusinessException(ErrorCodeEnum.GB10011045);
        }

        // 校验菜单URL是否唯一
        if (PublicUtil.isNotEmpty(dsmsMenu.getUrl())) {
            // 校验菜单URL唯一
            MenuCheckUrlDto menuCheckUrlDto = new MenuCheckUrlDto();
            menuCheckUrlDto.setMenuId(dsmsMenu.getId());
            menuCheckUrlDto.setUrl(dsmsMenu.getUrl());
            Preconditions.checkArgument(checkMenuUrl(menuCheckUrlDto),
                    csunMessageSource.getMessage("menu.url.exist", null));
        }

        // 校验菜单编码是否唯一
        if (PublicUtil.isNotEmpty(dsmsMenu.getMenuCode())) {
            MenuCheckCodeDto menuCheckCodeDto = new MenuCheckCodeDto();
            menuCheckCodeDto.setMenuId(dsmsMenu.getId());
            menuCheckCodeDto.setMenuCode(dsmsMenu.getMenuCode());
            Preconditions.checkArgument(checkMenuCode(menuCheckCodeDto),
                    csunMessageSource.getMessage("menu.code.exist", null));
        }

        dsmsMenu.setUpdateInfo(login);

        return dsmsMenuMapper.updateByPrimaryKeySelective(dsmsMenu);
    }

    /**
     * 判断此菜单是否有子节点
     *
     * @param pid    the parent id
     *
     * @return  the boolean
     */
    @Override
    public boolean checkMenuHasChildMenu(Long pid) {

        Preconditions.checkArgument(pid != null, "菜单pid不能为空");

        DsmsMenu dsmsMenu = new DsmsMenu();
        dsmsMenu.setStatus(MenuStatusEnum.ENABLE.getType());
        dsmsMenu.setPid(pid);

        return mapper.selectCount(dsmsMenu) > 0;
    }

    /**
     * 根据id删除菜单
     *
     * @param id    the id
     * @param login the login user
     *
     * @return  the int.
     */
    @Override
    public Integer deleteMenuById(Long id, DsmsUser login) {

        // 校验菜单Id是否为空
        Preconditions.checkArgument(id != null,
                csunMessageSource.getMessage("menu.id.null", null));

        int result;
        // 获取当前菜单信息
        DsmsMenu menuQuery = new DsmsMenu();
        menuQuery.setId(id);
        menuQuery = mapper.selectOne(menuQuery);
        if (PublicUtil.isEmpty(menuQuery)) {
            throw new BusinessException(ErrorCodeEnum.GB10011043);
        }

        // 判断是否分配了角色
        DsmsRoleMenu dsmsRoleMenu = new DsmsRoleMenu();
        dsmsRoleMenu.setMenuId(id);
        int count = dsmsRoleMenuService.selectCount(dsmsRoleMenu);
        if (count > 0) {
            throw new BusinessException(ErrorCodeEnum.GB10011050);
        }

        // 删除菜单与角色的关联关系
        dsmsRoleMenu = new DsmsRoleMenu();
        dsmsRoleMenu.setMenuId(id);
        dsmsRoleMenuService.delete(dsmsRoleMenu);

        // 删除菜单
        result = dsmsMenuMapper.deleteByPrimaryKey(id);
        if (result < 1) {
            throw new BusinessException(ErrorCodeEnum.GB10011051);
        }

        // 删除权限
        // TODO 应该先查询再删除

        // 修改当前删除菜单的父菜单是否是叶子节点
        DsmsMenu updateParentUacMenu = new DsmsMenu();
        updateParentUacMenu.setId(menuQuery.getPid());
        updateParentUacMenu.setLeaf(MenuConstant.MENU_LEAF_YES);
        // 是二三级
        if (Objects.equals(MenuConstant.MENU_LEVEL_TWO, menuQuery.getLevel()) || Objects.equals(MenuConstant.MENU_LEVEL_THREE, menuQuery.getLevel())) {
            // 查询是否是叶子节点
            count = dsmsMenuMapper.selectMenuChildCountByPid(menuQuery.getPid());
            if (count == 0) {
                dsmsMenuMapper.updateByPrimaryKeySelective(updateParentUacMenu);
            }
        }

        return result;
    }

    private List<MenuVo> getMenuVo(List<DsmsMenu> list) {
        List<MenuVo> menuVoList = Lists.newArrayList();
        for (DsmsMenu menu : list) {
            MenuVo menuVo = new MenuVo();
            BeanUtils.copyProperties(menu, menuVo);
            menuVo.setUrl(menu.getUrl());
            menuVo.setMenuName(menu.getMenuName());
            menuVoList.add(menuVo);
        }
        return menuVoList;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<DsmsMenu> getAllParentMenuByMenuId(Long menuId) {
        DsmsMenu menuQuery = new DsmsMenu();
        menuQuery.setId(menuId);
        menuQuery = mapper.selectOne(menuQuery);
        List<DsmsMenu> menuList = Lists.newArrayList();
        menuList = buildParentNote(menuList, menuQuery);
        return menuList;
    }

    @Override
    public List<Long> getCheckedMenuList(Long roleId) {

        if (roleId == null) {
            throw new BusinessException(ErrorCodeEnum.GB10021002);
        }
        return dsmsMenuMapper.getCheckedMenuList(roleId);
    }

    @Override
    public List<DsmsMenu> queryRoleMenuListWithPage(MenuQueryDto menuQueryDto) {
        return null;
    }

    /**
     * 根据菜单Id列表生成菜单树
     *
     * @param menuIdList	the menu id list
     *
     * @return	the bind auth vo
     */
    @Override
    public BindAuthVo getMenuTreeByMenuIdList(List<Long> menuIdList) {

        BindAuthVo bindAuthVo = new BindAuthVo();
        List<DsmsMenu> menuList;

        if (PublicUtil.isEmpty(menuIdList)) {
            // 查询所有的菜单信息
            menuList = selectAll();
        } else {
            MenuQueryDto menuQueryDto = new MenuQueryDto();
            menuQueryDto.setPageSize(65535);
            menuQueryDto.setMenuIds(StringUtils.join(menuIdList, ","));
            menuList = queryMenuList(menuQueryDto);
        }

        // 合并菜单和按钮权限 递归生成树结构
        List<MenuVo> menuVoList = getAuthList(menuList);

        List<MenuVo> tree = TreeUtil.getChildMenuVos(menuVoList, 0L);

        bindAuthVo.setAuthTree(tree);

        bindAuthVo.setCheckedAuthList(menuIdList);

        return bindAuthVo;
    }

    /**
     * 生成列表
     *
     * @param menuList  the menu list
     *
     * @return  the menu vo list.
     */
    @Override
    public List<MenuVo> getAuthList(List<DsmsMenu> menuList) {

        List<MenuVo> menuVoList = Lists.newArrayList();
        MenuVo menuVo;
        for (DsmsMenu menu : menuList) {
            menuVo = new MenuVo();
            BeanUtils.copyProperties(menu, menuVo);
            menuVo.setRemark("menu");
            menuVoList.add(menuVo);
        }

        return menuVoList;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<DsmsMenu> getAllChildMenuByMenuId(Long menuId, String menuStatus) {
        DsmsMenu menuQuery = new DsmsMenu();
        menuQuery.setId(menuId);
        menuQuery = mapper.selectOne(menuQuery);
        List<DsmsMenu> uacMenuList = Lists.newArrayList();
        uacMenuList = buildNode(uacMenuList, menuQuery, menuStatus);
        return uacMenuList;
    }

    @Override
    public int enableMenuList(List<DsmsMenu> menuList, DsmsUser login) {
        DsmsMenu menuUpdate = new DsmsMenu();
        int sum = 0;
        for (DsmsMenu menu : menuList) {
            menuUpdate.setId(menu.getId());
            menuUpdate.setVersion(menu.getVersion() + 1);
            menuUpdate.setStatus(MenuStatusEnum.ENABLE.getType());
            menuUpdate.setLastOperator(login.getLoginName());
            menuUpdate.setLastOperatorId(login.getId());
            menuUpdate.setUpdateTime(new Date());
            int result = mapper.updateByPrimaryKeySelective(menuUpdate);
            if (result > 0) {
                sum += 1;
            } else {
                throw new BusinessException(ErrorCodeEnum.GB10011047);
            }
        }
        return sum;
    }

    @Override
    public int disableMenuList(List<DsmsMenu> menuList, DsmsUser login) {
        DsmsMenu menuUpdate = new DsmsMenu();
        int sum = 0;
        for (DsmsMenu menu : menuList) {
            menuUpdate.setId(menu.getId());
            menuUpdate.setVersion(menu.getVersion() + 1);
            menuUpdate.setStatus(MenuStatusEnum.DISABLE.getType());
            menuUpdate.setLastOperator(login.getLoginName());
            menuUpdate.setLastOperatorId(login.getId());
            menuUpdate.setUpdateTime(new Date());
            int result = mapper.updateByPrimaryKeySelective(menuUpdate);
            if (result > 0) {
                sum += 1;
            } else {
                throw new BusinessException(ErrorCodeEnum.GB10011047);
            }
        }
        return sum;
    }

    /**
     * 递归获取菜单的子菜单
     */
    private List<DsmsMenu> buildNode(List<DsmsMenu> uacMenuList, DsmsMenu uacMenu, String menuStatus) {
        List<DsmsMenu> uacMenuQueryList = mapper.select(uacMenu);
        DsmsMenu menuQuery;
        for (DsmsMenu menu : uacMenuQueryList) {
            // 启用状态
            if (menuStatus.equals(menu.getStatus()) && !MenuConstant.MENU_LEVEL_ROOT.equals(menu.getLevel())) {
                uacMenuList.add(menu);
            }
            menuQuery = new DsmsMenu();
            menuQuery.setPid(menu.getId());
            buildNode(uacMenuList, menuQuery, menuStatus);
        }
        return uacMenuList;
    }

    /**
     * 递归获取菜单的父菜单
     */
    private List<DsmsMenu> buildParentNote(List<DsmsMenu> menuList, DsmsMenu uacMenu) {
        List<DsmsMenu> uacMenuQueryList = mapper.select(uacMenu);
        DsmsMenu menuQuery;
        for (DsmsMenu menu : uacMenuQueryList) {
            if (MenuStatusEnum.DISABLE.getType().equals(menu.getStatus()) && !MenuConstant.MENU_LEVEL_ROOT.equals(menu.getLevel())) {
                // 禁用状态
                menuList.add(menu);
            }
            menuQuery = new DsmsMenu();
            menuQuery.setId(menu.getPid());
            buildParentNote(menuList, menuQuery);
        }
        return menuList;
    }

    /**
     * 查询菜单列表
     *
     * @param menuQueryDto the menu query dto
     * @return the list.
     */
    @Override
    public List<DsmsMenu> queryMenuList(MenuQueryDto menuQueryDto) {

        return dsmsMenuMapper.queryMenuList(menuQueryDto);
    }
}
